﻿using System;
using System.Collections.Generic;
using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SilverMindMap.Core.Model;
using SilverMindMap.Core.Tests.TestHelpers;

namespace SilverMindMap.Core.Tests.Model
{
    [TestClass]
    public class MindMapObserverTests 
    {
        [TestMethod]
        [Tag(TestTags.BVT)]
        public void NodeAddedRaisedTest()
        {
            var mindMap = new MindMap();
            var observer = new MindMapObserver(mindMap);

            var allNotifications = new List<Tuple<object, NodeEventArgs>>();
            observer.NodeAdded += (s, a) => allNotifications.Add(Tuple.Create(s, a));

            var node1 = new Node("A");
            var node2 = new Node("B");
            mindMap.AddNode(node1);
            mindMap.AddNode(node2);
            mindMap.RemoveNode(node1);
            mindMap.AddNode(node1);

            var expectedNodes = new Node[] { node1, node2, node1 };
            
            Assert.AreEqual(expectedNodes.Length, allNotifications.Count);
            
            for (int i = 0; i < allNotifications.Count; i++)
            {
                Assert.AreSame(observer, allNotifications[i].Item1, "Invalid sender for event {0}", i);
                Assert.AreSame(expectedNodes[i], allNotifications[i].Item2.Node, "Invalid event args for event {0}", i);
            }
        }

        [TestMethod]
        [Tag(TestTags.BVT)]
        public void NodeRemovedRaisedTest()
        {
            var mindMap = new MindMap();
            var observer = new MindMapObserver(mindMap);

            var allNotifications = new List<Tuple<object, NodeEventArgs>>();
            observer.NodeRemoved += (s, a) => allNotifications.Add(Tuple.Create(s, a));

            var node1 = new Node("A");
            var node2 = new Node("B");
            var node3 = new Node("C");
            mindMap.AddNode(node1);
            mindMap.RemoveNode(node1);
            mindMap.AddNode(node2);
            mindMap.AddNode(node3);
            mindMap.RemoveNode(node3);

            var expectedNodes = new Node[] { node1, node3 };

            Assert.AreEqual(expectedNodes.Length, allNotifications.Count);

            for (int i = 0; i < allNotifications.Count; i++)
            {
                Assert.AreSame(observer, allNotifications[i].Item1, "Invalid sender for event {0}", i);
                Assert.AreSame(expectedNodes[i], allNotifications[i].Item2.Node, "Invalid event args for event {0}", i);
            }
        }

        [TestMethod]
        [Tag(TestTags.BVT)]
        public void LinkAddedRaisedTest()
        {
            var mindMap = new MindMap();
            var nodeB = new Node("B");
            mindMap.AddNode(nodeB);

            var observer = new MindMapObserver(mindMap);
            var allNotifications = new List<Tuple<object, LinkEventArgs>>();
            observer.LinkAdded += (s, a) => allNotifications.Add(Tuple.Create(s, a));

            var nodeA = new Node("A");            
            var nodeC = new Node("C");
            var nodeD = new Node("D");
            var nodeE = new Node("E");

            var linkA_B = nodeA.AddLink(nodeB);
            mindMap.AddNode(nodeA);               // This should raise notification for previously added link
            
            var linkB_C = nodeB.AddLink(nodeC);   // This should raise notification, even though node 3 is not in the map
            mindMap.AddNode(nodeD);
            var linkD_A = nodeD.AddLink(nodeA);   // This should raise notification
            nodeE.AddLink(nodeC);                 // This should not raise notification, because node5 is not in the map

            var expectedLinks = new Link[] { linkA_B, linkB_C, linkD_A };

            Assert.AreEqual(expectedLinks.Length, allNotifications.Count);

            for (int i = 0; i < allNotifications.Count; i++)
            {
                Assert.AreSame(observer, allNotifications[i].Item1, "Invalid sender for event {0}", i);
                Assert.AreSame(expectedLinks[i], allNotifications[i].Item2.Link, "Invalid event args for event {0}", i);
            }
        }

        [TestMethod]
        [Tag(TestTags.BVT)]
        public void LinkRemovedRaisedTest()
        {
            var mindMap = new MindMap();
            var observer = new MindMapObserver(mindMap);

            var allNotifications = new List<Tuple<object, LinkEventArgs>>();
            observer.LinkRemoved += (s, a) => allNotifications.Add(Tuple.Create(s, a));

            var nodeA = new Node("A"); mindMap.AddNode(nodeA);
            var nodeB = new Node("B"); mindMap.AddNode(nodeB);
            var nodeC = new Node("C"); mindMap.AddNode(nodeC);
            var nodeD = new Node("D"); mindMap.AddNode(nodeD);
            var nodeE = new Node("E");
            var nodeF = new Node("F");

            var linkA_B = nodeA.AddLink(nodeB);
            var linkB_C = nodeB.AddLink(nodeC);
            var linkB_E = nodeB.AddLink(nodeE);
            var linkA_D = nodeA.AddLink(nodeD);
            var linkF_A = nodeE.AddLink(nodeA);

            nodeA.RemoveLink(linkA_D);
            mindMap.RemoveNode(nodeB);
            nodeF.RemoveLink(linkF_A);
            mindMap.RemoveNode(nodeA);

            var expectedLinks = new Link[] { linkA_D, linkB_C, linkB_E, linkA_B };

            Assert.AreEqual(expectedLinks.Length, allNotifications.Count);

            for (int i = 0; i < allNotifications.Count; i++)
            {
                Assert.AreSame(observer, allNotifications[i].Item1, "Invalid sender for event {0}", i);
                Assert.AreSame(expectedLinks[i], allNotifications[i].Item2.Link, "Invalid event args for event {0}", i);
            }
        }

        [TestMethod]
        public void DisposeTest()
        {
            var mindMap = new MindMap();
            var nodeA = new Node("A");
            var nodeB = new Node("B");
            mindMap.AddNode(nodeA);
            mindMap.AddNode(nodeB);
            nodeA.AddLink(nodeB);

            var observer = new MindMapObserver(mindMap);            
            var observerReference = new WeakReference(observer);

            var nodeC = new Node("C");
            mindMap.AddNode(nodeC);
            nodeB.AddLink(nodeC);
            
            // After disposing, observer should unregister handlers to the mindMap, so we should be able to collect it.
            observer.Dispose();
            observer = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();

            Assert.IsFalse(observerReference.IsAlive);
        }
    }
}
